#include "config.h"

#define DBG_SUBSYS S_LIBSTORAGE

#include "gdsl.h"
#include "lsv.h"
#include "coroutine.h"

// extern int _align_page_write(void *arg);

typedef struct {
        gdsl_rbtree_t tree;
        uint64_t lba;
        co_mq_t mq;
} lba_fifo_t;

static void _tree_free(gdsl_element_t e) {
        lba_fifo_t *e1 = e;
        static uint64_t free_count = 0;

        free_count++;
        DERROR("lba %llu %p free %llu\n", (LLU)e1->lba, e1, (LLU)free_count);

        YASSERT(e1 == e1->mq.swf_arg);
        YASSERT(e1->mq.queue.count == 0);
        // co_mq_destroy(&e1->mq);

        yfree((void **)&e1);
}

static long int _tree_cmp(const gdsl_element_t s1, void *s2) {
        lba_fifo_t *e1 = s1;
        lba_fifo_t *e2 = s2;
        return e1->lba - e2->lba;
}

int lba_tree_init(gdsl_rbtree_t *_tree) {
        gdsl_rbtree_t tree;

        *_tree = NULL;

        tree = gdsl_rbtree_alloc("APW", NULL, _tree_free, _tree_cmp);

        *_tree = tree;
        return 0;
}

int lba_tree_destroy(gdsl_rbtree_t tree) {
        // TODO
        return 0;
}

static int _mq_swf(void *arg) {
        // TODO if last one, delete from tree
        lba_fifo_t *fifo = arg;

        YASSERT(fifo->mq.queue.count == 0);

        lba_fifo_t s;
        s.lba = fifo->lba;
        gdsl_rbtree_delete(fifo->tree, &s);
        return 0;
}

int lba_tree_insert(gdsl_rbtree_t tree, co_func_t func, align_page_write_ctx_t *ctx, int need_free) {
        int ret;
        const char *name = "LBA";
        static uint64_t pages = 0;
        lba_fifo_t *e;

        YASSERT(ctx->lba % LSV_PAGE_SIZE == 0);

        lba_fifo_t s;
        s.lba = ctx->lba;
        gdsl_element_t found = gdsl_rbtree_search(tree, NULL, (void *)&s);
        if (found) {
                e = (lba_fifo_t *)found;

                co_mq_offer(&e->mq, name, func, ctx, need_free);
        } else {
                //
                ret = ymalloc((void **)&e, sizeof(lba_fifo_t));
                if (unlikely(ret))
                        YASSERT(0);

                e->tree = tree;
                e->lba = ctx->lba;

                co_mq_init(&e->mq, name);
                e->mq.swf = _mq_swf;
                e->mq.swf_arg = e;

                gdsl_rbtree_insert(tree, (void *)e, &ret);
                YASSERT(ret == GDSL_INSERTED);

                co_mq_offer(&e->mq, name, func, ctx, need_free);
        }

        // TODO
        pages++;
        ulong count = gdsl_rbtree_get_size(tree);
        DERROR("lba %llu %p (%p) size %lu page %llu depth %u\n", (LLU)ctx->lba, e, found, count,
               (LLU)pages, e->mq.queue.count);

        return 0;
}
